package 一致性哈希算法;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 25228
 * Date: 2024-11-10
 * Time: 15:06
 */
public class Main {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        HashRing hashRing=new HashRing(200);
        hashRing.add("server1");
        hashRing.add("server2");
        hashRing.add("server3");
        System.out.println(hashRing.get("Uer1"));
        hashRing.remove("server1");
        System.out.println(hashRing.get("Uer1"));
    }
}


class HashRing{
    static MessageDigest md = null;
    static SortedMap<Long,String> circle=new TreeMap<>();
    static int replicas=20;

    //构造函数，需要指定每个服务器的虚拟节点数
    HashRing(int replicas) throws NoSuchAlgorithmException {
        this.replicas=replicas;
        this.md = MessageDigest.getInstance("SHA-256");
    }

    //向环中添加一个新的服务器。
    static void add(String serverName){
        for (int i = 0; i < replicas; i++) {
            String node=serverName + "#" + i;//生成虚拟节点
            long hash=hash(node.getBytes(StandardCharsets.UTF_8));
            circle.put(hash,node);
        }
    }

    //从环中移除指定的服务器。
    static void remove(String serverName){
        for (int i = 0; i < replicas; i++) {
            String node=serverName + "#" + i;//删除虚拟节点
            long hash=hash(node.getBytes(StandardCharsets.UTF_8));
            circle.remove(hash);
        }
    }


    //给定一个待添加的key，返回它应被分配到的服务器名
    static String get(String key){
        long hash=hash(key.getBytes(StandardCharsets.UTF_8));
        SortedMap taiMap=new TreeMap();
        taiMap=circle.tailMap(hash);
        if(taiMap.isEmpty()){
            return circle.get(circle.firstKey());
        }else {
            return circle.get(taiMap.firstKey());
        }
    }


    //给定字节数组表示的data，计算其hash值
    static long hash(byte[] data){
        try {
            md.reset();
            md.update(data);
            byte[] hashBytes = md.digest();
            long hash = ((long) (hashBytes[0] & 0xFF) << 56)
                    | ((long) (hashBytes[1] & 0xFF) << 48)
                    | ((long) (hashBytes[2] & 0xFF) << 40)
                    | ((long) (hashBytes[3] & 0xFF) << 32)
                    | ((hashBytes[4] & 0xFF) << 24)
                    | ((hashBytes[5] & 0xFF) << 16)
                    | ((hashBytes[6] & 0xFF) << 8)
                    | (hashBytes[7] & 0xFF);
            return Math.abs(hash);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
